home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 98
/
Skunkware 98.iso
/
src
/
net
/
bind-contrib.tar.gz
/
bind-contrib.tar
/
contrib
/
ninit
/
ninit.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-10-25
|
11KB
|
504 lines
/*
* Named init --- sits around and restarts named when necessary
*
* Written by Theodore Ts'o. Copyright 1991.
*
* Use this code however you want, as long as you don't try to make
* money off of it and as long as you don't claim it's yours.
*
* $Header: /proj/src/isc/cvs-1/bind/contrib/ninit/ninit.c,v 8.1 1996/10/25 04:57:46 vixie Exp $
* $Source: /proj/src/isc/cvs-1/bind/contrib/ninit/ninit.c,v $
*
* Note that ninit requires that named be modified so that it accepts
* the -n option. This option to named asks named not to fork into
* the background when it is started up. This is necessary because
* ninit wants to be notified when the named process exits.
*
* Usage: ninit [options] [named boot file]
*
* Options:
* -n <negative niceness> Run the named process with the nice level
* *reduced* argument level. This is useful
* on mailhub, when you have 30+ sendmail
* processes and one named process, and you
* want to give the named process a
* higher priority.
*
* -s <stats interval> This option controls how often (in
* seconds) named should be strobed to
* update the named.stats file.
*
* -c <check interval> This option controls how often (in
* seconds) ninit should check to make
* sure named is still working.
*
* -f <syslog facility> This option controls which syslog facility
* ninit should use.
*
* -d <debug level> This option is passed to named to turn
* on nameserver debugging.
*
* -N This option tells ninit not fork and go
* into the background when it is started
* up.
*
*/
#include <stdio.h>
#include <sys/types.h>
#include <syslog.h>
#include <errno.h>
#include <signal.h>
#include <netdb.h>
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/nameser.h>
#include <netinet/in.h>
#include <resolv.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/resource.h>
/*
* Configuration section. It should be fairly obvious.....
*
* FORKING_NAMED is intended for site who do not have source code to
* their named, and so cannot add the -n option to named.
*
* TEST_HOST specifies the host which ninit should try to resolve when
* checking to see if named is working.
*/
#define TEST_HOST "MIT.EDU"
#define NAMED "/etc/named"
#define NAMED_PID_FILE "/etc/named.pid"
#define PID_FILE "/etc/ninit.pid"
#define DEFAULT_FACILITY LOG_LOCAL4
/* #define FORKING_NAMED */
extern int h_errno;
int named_pid = -1;
int terminating_named = 0;
int named_restarting = 0;
int child_died = 0;
int stats_timer = 0;
int check_timer = 0;
char *named_file = 0;
int named_nice = 0;
int debug = 0;
int syslog_facility = DEFAULT_FACILITY;
char *progname = 0;
int nofork = 0;
int stats_interval = 300;
int check_interval = 60;
struct in_addr local_addr;
void start_named(), PRS(), usage();
int decode_facility();
struct code {
char *name;
int facility;
};
struct code FacNames[] = {
"kern", LOG_KERN,
"user", LOG_USER,
"mail", LOG_MAIL,
"daemon", LOG_DAEMON,
"auth", LOG_AUTH,
"syslog", LOG_SYSLOG,
"lpr", LOG_LPR,
#ifdef LOG_NEWS
"news", LOG_NEWS,
#endif
#ifdef LOG_UUCP
"uucp", LOG_UUCP,
#endif
"local0", LOG_LOCAL0,
"local1", LOG_LOCAL1,
"local2", LOG_LOCAL2,
"local3", LOG_LOCAL3,
"local4", LOG_LOCAL4,
"local5", LOG_LOCAL5,
"local6", LOG_LOCAL6,
"local7", LOG_LOCAL7,
"security", LOG_AUTH,
#ifdef LOG_MARK
"mark", LOG_MARK,
#endif
NULL, -1
};
int alarm_chk()
{
int named_active = 1;
int do_check = 0;
int next_alarm;
if ((named_pid <= 0) || named_restarting || terminating_named) {
named_active = 0;
}
/*
* We could put something here to increment the timers by alarm(0),
* but we won't bother for now.
*
* Check to see if the timers have expired; if so, run the events
*/
if (named_active && (stats_timer <= 0)) {
(void) rename("/usr/tmp/named.stats",
"/usr/tmp/named.stats.old");
kill(named_pid, SIGIOT);
stats_timer = stats_interval;
}
if (named_active && (check_timer <= 0)) {
/*
* We run this one after restarting the alarm because
* it might take a while
*/
do_check++;
check_timer = check_interval;
}
/*
* Now figure out when the next time we need to run.
*/
if (stats_timer < check_timer)
next_alarm = stats_timer;
else
next_alarm = check_timer;
stats_timer -= next_alarm;
check_timer -= next_alarm;
alarm(next_alarm);
/*
* Now, perform the named check if necessary
*/
if (do_check) {
(void) gethostbyname(TEST_HOST);
if (h_errno == TRY_AGAIN) {
syslog(LOG_ERR, "Named hosed! Restarting...");
restart_named();
}
}
}
int restart_named()
{
if ((named_pid <= 0) || named_restarting || terminating_named)
return;
terminating_named++;
kill(named_pid, SIGTERM);
}
int die()
{
syslog(LOG_NOTICE, "/etc/ninit received SIGTERM, exiting");
if (named_pid > 0) {
kill(named_pid, SIGTERM);
syslog(LOG_NOTICE, "named killed as part of ninit shutdown");
}
(void) unlink(NAMED_PID_FILE);
(void) unlink(PID_FILE);
exit(0);
}
int mourner()
{
child_died++;
}
main(argc, argv)
int argc;
char **argv;
{
int pid;
union wait status;
PRS(argc, argv);
daemon_setup();
openlog("ninit", LOG_PID|LOG_CONS, syslog_facility);
syslog(LOG_INFO, "ninit started, pid = %d", getpid());
signal(SIGALRM, alarm_chk);
signal(SIGHUP, restart_named);
signal(SIGTERM, die);
signal(SIGCHLD, mourner);
start_named();
alarm_chk();
while (1) {
pid = wait(&status);
if (pid == -1) {
if (errno == ECHILD) {
named_pid = -1;
start_named();
continue;
} else {
syslog(LOG_CRIT,
"Error in wait: %s",
sys_errlist[errno]);
sleep(120);
continue;
}
}
named_pid = -1;
if (terminating_named || (status.w_termsig == SIGTERM)) {
terminating_named = 0;
} else if (status.w_termsig) {
syslog(LOG_ERR, "Named terinated with signal %d%s",
status.w_termsig,
status.w_coredump ? " (core dumped)" : "");
} else {
syslog(status.w_retcode ? LOG_ERR : LOG_NOTICE,
"named termined with exit status %d",
status.w_retcode);
}
start_named();
}
}
void start_named()
{
int pid;
int tries = 3;
FILE *f;
char debug_buf[24];
char *argv[10];
int argc = 0;
if (f = fopen(NAMED_PID_FILE, "r")) {
fscanf(f, "%d", &pid);
fclose(f);
if ((pid > 0) && !kill(pid, 0)) {
/* There is a running named already, kill it. */
syslog(LOG_WARNING,
"Killing already existing named, pid = %d",
pid);
kill(pid, 15);
}
}
if (named_restarting++ > 3) {
syslog(LOG_ALERT,
"Couldn't start named after three tries, sleeping");
sleep(180);
named_restarting = 1;
}
child_died = 0;
if ((pid = vfork()) == 0) {
argv[argc++] = "named";
if (debug) {
argv[argc++] = "-d";
sprintf(debug_buf, "%d", debug);
argv[argc++] = debug_buf;
}
#ifdef FORKING_NAMED
else
argv[argc++] = "-d";
#else
argv[argc++] = "-n";
#endif
argv[argc++] = named_file;
argv[argc++] = 0;
execv(NAMED, argv);
syslog(LOG_ERR, "Couldn't start named: %s",
sys_errlist[errno]);
exit(1);
} else if (pid == -1) {
syslog(LOG_ERR, "Couldn't fork to start named: %s",
sys_errlist[errno]);
sleep(180);
return;
} else {
syslog(LOG_DEBUG, "Named process started, pid = %d", pid);
named_pid = pid;
setpriority(PRIO_PROCESS, pid, named_nice);
named_restarting++;
/* Stall until the named is really working */
do {
if (child_died)
return;
(void) gethostbyname(TEST_HOST);
} while (h_errno == TRY_AGAIN);
syslog(LOG_INFO, "Named successfully started, pid = %d\n",
pid);
named_restarting = 0;
#ifdef FORKING_NAMED
/* Turn off debugging */
if (!debug)
kill(named_pid, SIGUSR2);
#endif
/*
* Reset timers...
*/
stats_timer = stats_interval;
check_timer = check_interval;
alarm_chk();
}
}
void PRS(argc, argv)
int argc;
char **argv;
{
register char *word, ch;
char *buf;
char *fac = NULL;
local_addr.s_addr = inet_addr("127.0.0.1");
res_init();
_res.nscount = 1; /* One nameserver --- the local one */
_res.nsaddr.sin_addr = local_addr;
_res.retry = 2;
_res.retrans = RES_TIMEOUT;
progname = *argv++;
while (word = *argv++) {
if (*word == '-') {
word++;
while (word && (ch = *word++)) {
switch(ch){
case 'n': /* Niceness */
if (*word)
buf = word;
else
buf = *argv++;
if (!buf)
usage();
named_nice = 0 - atoi(buf);
word = 0;
break;
case 's': /* Stats int. */
if (*word)
buf = word;
else
buf = *argv++;
if (!buf)
usage();
stats_interval = atoi(buf);
word = 0;
break;
case 'c': /* Check int. */
if (*word)
buf = word;
else
buf = *argv++;
if (!buf)
usage();
check_interval = atoi(buf);
word = 0;
break;
case 'f': /* Syslog facility */
if (*word)
fac = word;
else
fac = *argv++;
if (!fac)
usage();
word = 0;
break;
case 'd': /* Debug */
if (*word)
buf = word;
else
buf = *argv++;
if (!buf)
usage();
debug = atoi(buf);
word = 0;
break;
case 'N': /* Nofork */
nofork++;
break;
default:
usage();
}
}
} else {
if (named_file)
usage();
else
named_file = word;
}
}
if (fac) {
syslog_facility = decode_facility(fac);
if (syslog_facility < 0) {
fprintf(stderr, "%s is not a valid facility\n", fac);
exit(1);
}
}
}
void usage()
{
fprintf(stderr,
"Usage: %s [OPTIONS] named_boot_file\n\n", progname);
fprintf(stderr, "Where the options can be: \n");
fprintf(stderr, "\t-d debug_level\tTurns on debugging\n");
fprintf(stderr, "\t-n nice arg\tRenices the named by -nice arg\n");
fprintf(stderr, "\t-f facility\tUses the specified syslog facility\n");
fprintf(stderr, "\t-s stats_interval\tSpecifies the statistics polling interval\n");
fprintf(stderr, "\t-c check_interval\tSpecifies the named check interval\n");
fprintf(stderr, "\t-N\t\tCauses ninit not to fork on startup.\n");
exit(1);
}
daemon_setup()
{
int n, pid;
FILE *f;
if (f = fopen(PID_FILE, "r")) {
fscanf(f, "%d", &pid);
fclose(f);
if (!kill(pid, 0)) {
fprintf(stderr,
"ninit is already running on pid = %d\n",
pid);
exit(1);
}
}
if (!nofork) {
if (fork() > 0)
exit(0);
n = open("/dev/null", O_RDONLY);
(void) dup2(n, 0);
(void) dup2(n, 1);
(void) dup2(n, 2);
if (n > 2)
(void) close(n);
}
if ((f = fopen(PID_FILE, "w")) == NULL)
syslog(LOG_ERR, "Couldn't open pid file for write, %s",
sys_errlist[errno]);
fprintf(f, "%d\n", getpid());
fclose(f);
}
int decode_facility(name)
char *name;
{
char buf[40], *src, *dest;
struct code *p;
int i;
for (src = name, dest = buf, i = 0; *src && i < 40; src++, dest++, i++)
*dest = (isupper(*src)) ? tolower(*src) : *src;
for (p = FacNames; p->name; p++)
if (!strcmp(buf, p->name))
return(p->facility);
return(-1);
}